#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "scheduler.h"
#include "command.h"
#include "console.h"
#include "libasm.h"
#include "kernel.h"
#include "shm.h"
#include "defs.h"

#define MIN_YEAR 1969
#define MAX_YEAR 2038
#define DATE_PARAM_COUNT 7

#define KBRD_INTRFC 0x64	/* puerto IO del teclado: data y control */

/* bits de interface del teclado */
#define KBRD_BIT_KDATA 0
#define KBRD_BIT_UDATA 1
#define KBRD_IO 0x60	/* puerto IO de teclado */
#define KBRD_RESET 0xFE	/* comando para resetear CPU */

/* Setear bit n a 1 */
#define bit(n) (1<<(n))

/* Checkear si el bit n tiene flags seteadas */
#define check_flag(flags, n) ((flags) & bit(n))

#define BETWEEN(x, min, max) ((x) >= (min) && (x) <= (max))
#define IS_LEAP_YEAR(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))

typedef unsigned short u16int;
typedef unsigned char  u8int;

static int validate_date(unsigned int day, unsigned int month,
		unsigned int year);
static int int2bcd(int n);

extern int last100[100];

int
ps(int argc, char *argv[])
{
	unsigned int i, j, first = 1;
	int tty;

	puts("+--------+--------+-----------+--------+-----------------+\n");
	puts("| PID    | PPID   | STATUS    | TTY    | CMD             |\n");
	puts("+--------+--------+-----------+--------+-----------------+\n");

	i = j = get_task_pid(NULL);
	while (i != j || first)
	{
		first = 0;
		printf("|%7u |", i);
		printf("%7u |", get_task_ppid(i) );
		printf("%10s |", get_pid_status(i));
		printf(" %c", is_bkg_task(i) ? '*' : ' ');
		if ((tty = get_task_tty(i)) == -1)
			printf("-     |");
		else
			printf("tty%-2u |", tty);
		printf(" %15s |\n", get_task_cmd(i));
		i = get_next_task_pid(i);
	}
	puts("+--------+--------+-----------+--------+-----------------+\n");

	putchar('\n');
	_doexit(0);
	return 0;
}

int
top(int argc, char *argv[])
{
	unsigned int i, j;
	int tty, first = 1;
	int vec[TASKS_COUNT]={0};

	while(1)
	{
		if(!is_bkg_task(0))
			clear_screen(get_console_video(get_task_tty(0)));
		for(i=0; i<100; i++)
			vec[last100[i]] = vec[last100[i]]+1;

	puts("+--------+--------+-----------+--------+-----------------+--------+\n");
	puts("| PID    | PRIO   | STATUS    | TTY    | CMD             |  %CPU  |\n");
	puts("+--------+--------+-----------+--------+-----------------+--------+\n");

		i = j = get_task_pid(NULL);
		while (i != j || first)
		{
			first = 0;
			printf("|%7u |", i);
			printf("%7u |", MAX_PRIO - get_task_prio(i) );
			printf("%10s |", get_pid_status(i));
			printf(" %c", is_bkg_task(i) ? '*' : ' ');
			if ((tty = get_task_tty(i)) == -1)
				printf("-     |");
			else
				printf("tty%-2u |", tty);
			printf(" %15s |", get_task_cmd(i));
			//Se imprime de esta forma para que no de warning al compilar
			printf(" %3d", vec[i]); putchar('%');puts("   |\n");
			i = get_next_task_pid(i);
		}
	puts("+--------+--------+-----------+--------+-----------------+--------+\n");
	first = 1;

	for(i=0; i<TASKS_COUNT; i++)
		vec[i] = 0;

	sleep(2);
	}

	putchar('\n');
	doexit(0);
	return 0;
}

int
kill(int argc, char *argv[])
{
	if (argc <= 1)
		printf("%s: pid [status]\n", argv[0]);
	else
	{
		if (argc == 2)
			argv[2] = "0";
		_Cli();
		//if (!_free_task(atoi(argv[1]), atoi(argv[2])))
			//printf("%s: No se encontro el proceso con pid %s",
					//argv[0], argv[1]);
		_free_task(atoi(argv[1]), atoi(argv[2]));
		_Sti();
	}

	putchar('\n');
	_doexit(0);
	return 0;
}


int
killall(int argc, char *argv[])
{
	int pid;
	if (argc <= 1)
		printf("%s: name [status]\n", argv[0]);
	else
	{
		if (argc == 2)
			argv[2] = "0";

		while ((pid = task_exists(argv[1], -1)) != 0)
			_free_task(pid, atoi(argv[2]));
	}

	putchar('\n');
	_doexit(0);
	return 0;
}

int
sleep_test(int argc, char *argv[])
{
	int i = 5;
	unsigned int interval = 1;
	if (argc >= 3)
	{
		i = atoi(argv[1]);
		interval = atoi(argv[2]);
	} else if (argc == 2)
		i = atoi(argv[1]);

	while(i > 0)
	{
		printf("%d\n", i);
		sleep(interval);
		i--;
	}

	putchar('\n');
	_doexit(0);
	return 0;
}


int
echo(int argc, char *argv[])
{
	int c;

	while ((c=getchar()) != KEY_ESC)
		putchar((char)c);

	puts("\nRecibi un EOF\n");

	_doexit(0);
	return 0;
}

int
setdate(int argc, char *argv[])
{
	/*
	 * Los siguientes arreglos contienen la informacin asociada a cada
	 * parametro de la fecha en el sig. orden:
	 *        dia, mes, siglo, anio, hora, minutos, segundos y '\0'
	 *
	 * ints: arreglo de valores enteros asociados
	 * regs: arreglo de nro. de registros del CMOS RTC asociados
	 * maxs: arreglo de valores mximos asociados
	 * strs: arreglo de descripciones a mostrar asociadas
	 */
	static int ints[8] = { 28, 8, 20, 6, 12, 30, 30, 0 };
	static int regs[8] = { 0x07, 0x08, 0x32, 0x09, 0x04, 0x02, 0x00, 0 };
	static int maxs[8] = { 31, 12, 20, 99, 23, 59, 59, 0 };
	static char *strs[8] = { "el dia", "el mes", "el anio", "el anio",
		"la hora", "los minutos", "los segundos", NULL };
	int i, year, pid;
	static char* aux[] = {"killall", "clock", NULL};

	puts("Cambiar fecha y hora del sistema\n");
	year = 0;
	i = 0;
	while (i < 7)
	{
		printf("Ingrese %s: ", strs[i]);

		if (i == 2)
		{
			year = getint(); /* BCD de anio */

			ints[2] = year / 100;
			ints[3] = year % 100;
			if (!BETWEEN(year, MIN_YEAR, MAX_YEAR))
				puts("Error: Fuera de rango. (1969-2038)\n");
			else if (!validate_date(ints[0], ints[1], year))
			{
				i = 0;
				puts("Fecha invalida.\n");
			}
			else
				i = 4;
		}
		else
		{
			ints[i] = getint(); /* demas BCDs */

			if (BETWEEN(ints[i], (i < 2 ? 1 : 0), maxs[i]))
				i++;
			else
				printf("Error: Fuera de rango. (%d-%d)\n",
						(i < 2 ? 1 : 0), maxs[i]);
		}
	}

	for (i = 0; i < 7; i++)
		writecmos(int2bcd(ints[i]), regs[i]);

	/* Registrar el nuevo programa */
	if((pid = new_task(killall, 2, aux, 0)) != 0)
		_waitpid(pid);
	new_task(clock, 0, NULL, 1);

	putchar('\n');
	_doexit(0);
	return 0;
}

int
getdate(int argc, char *argv[])
{
	int val[6];

	if (!gettime(&val[5], &val[4], &val[3], &val[2], &val[1], &val[0]))
		_doexit(1);

	printf("Fecha: %2.2d/%2.2d/%2.2d\n", val[3], val[4], val[5]);
	printf("Hora:  %2.2d:%2.2d:%2.2d\n", val[2], val[1], val[0]);

	putchar('\n');
	_doexit(0);
	return 0;
}

int
fibonacci(int argc, char *argv[])
{
	unsigned int num, pid;

	if (argc < 2)
	{
		printf("%s [num]\n", (argc < 1 ? "fibonacci" : argv[0]));
		_doexit(1);
		return 1;
	}

	num = atoi(argv[1]);
	pid = new_task(fibonacci_wrap, num, argv, 0);

	_waitpid(pid);

	_doexit(0);
	return 0;
}

int
fibonacci_wrap(int argc, char *argv[])
{
	/* delay */
	//sleep(1);

	printf("Fibonacci %-9d \n", argc);

	if (argc > 0)
	{
		if (argc > 1)
			_waitpid(new_task(fibonacci_wrap, argc - 2, argv, 0));
		_waitpid(new_task(fibonacci_wrap, argc - 1, argv, 0));
	}

	_doexit(0);
	return 0;
}

int
clear(int argc, char *argv[])
{
	clear_screen(get_console_video(get_task_tty(0)));
	_doexit(0);
	return 0;
}

int
count(int argc, char *argv[])
{
	int j = 0, i = 0;
	if (argc > 1)
		i = atoi(argv[1]);
	while (j++ < i)
		printf("%d ", j);

	putchar('\n');

	_doexit(0);
	return 0;
}

int
yes(int argc, char *argv[])
{
	char string[BUFFER_LENGTH+1];

	if (argc < 2)
	{
		printf("%s [num]\n", (argc < 1 ? "yes" : argv[0]));
		_doexit(1);
		return 1;
	}
	else
	{
		strcpy(string, argv[1]);
		do
		{
			puts(string);
			putchar('\n');
		}while(1);
	}

	_doexit(0);
	return 0;
}

static int
waitpid_test_child(int argc, char argv[])
{
   int i = 500;

   while(i--)
       puts("Proceso hijo- ");

   sleep(3);
   putchar('\n');
   _doexit(314);
   return 0;
}


int
waitpid_test(int argc, char *argv[])
{
   unsigned int pid, i = 500, status;

   puts("Primero debera ejecutar el hijo y una vez que este termine el "
           "padre\n");

   /* Registro la la otra funcin de testeo*/
   pid = new_task((programT) waitpid_test_child, 0, NULL, 0);

   /* Espero a que se ejecute la otra funcin de testeo */
   status = _waitpid(pid);

   while(i--)
       puts("Proceso padre-");

   putchar('\n');
   printf("Proceso hijo debera terminar su ejecucion con 314 y lo hizo "
           "con %d\n", status);
   _doexit(0);
   return 0;
}


static int
shm_test_child(int argc, char *argv[])
{
	int ppid;
	char * shm;
	int * sem;
	char buff[SHELL_CMD_BUFFER_SIZE];

	ppid = get_task_ppid(0);

	shm  = shmAt(ppid);
	sem = semGet(ppid);

	puts("Hijo: Antes de subir el semaforo\n");
	vSem(sem);
	puts("Hijo: Zona de codigo restringida por el semaforo\n");

	sleep(1);

	strcpy(buff, shm);
	printf("Hijo: recibio el string \"%s\" y lo invierte\n", buff);
	invierte(buff, shm, strlen(buff));

	pSem(sem);
	puts("Hijo: Salio de la zona de codigo restringida\n");
	sleep(3);

	shmDt(ppid);

	_doexit(0);
	return 0;
}

int
shm_test(int argc, char *argv[])
{
	int pid;
	char * shm;
	int * sem;

	if(argc < 2)
	{
		printf("%s [string]\n", (argc < 1 ? "shm_test" : argv[0]));
		_doexit(1);
		return 1;
	}

	pid = get_task_pid(NULL);
	shm  = (char *)shmAt(pid);
	sem = semGet(pid);

	pid = new_task(shm_test_child, 0, NULL, 0);

	puts("Padre: Antes de subir el semaforo\n");
	vSem(sem);
	puts("Padre: Zona de codigo restringida por el semaforo\n");

	sleep(1);
	printf("Padre: Copia \"%s\" a la shared memory\n", argv[1]);
	strcpy(shm, argv[1]);

	printf("Padre: copio %s\n", shm);
	pSem(sem);
	puts("Padre: Salio de la zona de codigo restringida\n");

	waitpid(pid);

	printf("Padre: string invertido \"%s\"\n", shm);
	sleep(1);

	shmDt(pid);

	_doexit(0);
	return 0;
}


int
malloc_test(int argc, char * argv[])
{
	char *maloqueado;

	if(argc<2)
	{
		printf("%s: string\n", argv[0]);
		_doexit(1);
		return 1;
	}

	printf("El string recibido es:\n%s\n", argv[1]);
	puts("Alocando espacio\n");
	maloqueado = malloc(strlen(argv[1]));

	puts("Copiando string al espacio alocado\n");
	strcpy(maloqueado, argv[1]);

	puts("El string alocado quedo asi:\n");
	printf("%s", maloqueado);
	putchar('\n');

	_doexit(0);
	return 0;
}

int
idle(int argc, char *argv[])
{
	puts("Empezando idle\n");
	while(1);

	_doexit(0);
	return 0;
}

int
test_args(int argc, char *argv[])
{
	int i = 0;
	printf("Cantidad de argumentos: %d\n", argc);
	while (i < argc)
	{
		printf("Argumento %d: %s\n", i + 1, argv[i]);
		i++;
	}
	puts("Fin de test_arg\n");

	_doexit(0);
	return 0;
}

int
fg(int argc, char *argv[])
{
	int pid;

	if (argc < 2)
	{
		printf("%s [num]\n", (argc < 1 ? "fg" : argv[0]));
		exit_task(1);
		return 1;
	}
	else
		if((pid = task_to_fg(argv[1])) > 0)
		{
			printf("%d %s: brought to foreground.\n", pid, argv[1]);
			waitpid(pid);
		}
		else
			printf("%s: doesn't exist in current tty\n", argv[1]);

	_doexit(0);
	return 0;
}

int
init(int argc, char *argv[])
{
	new_console(0, 0, 1);
	new_task(clock, 0, NULL, 1);

	/* A partir de ahora puedo empezar a conmutar y a recibir
	 * interrupciones */
	_Sti();


	while (1)
		;

	/* Nunca se llega a este punto */
	_doexit(0);
	return 0;
}

int
help(int argc, char *argv[])
{
	puts("\nAYUDA\n\n"
		 "clear        Limpiar la pantalla\n"
		 "clock        Carga el reloj de la barra del sistema\n"
		 "count N      Imprimir numeros enteros del 1 a N\n"
		 "echo         Repite lo ingresado por 'entrada estandar'\n"
		 "fibonacci    Genera un arbol de fibonacci de N niveles.\n"
		 "getdate      Mostrar la fecha y hora del sistema\n"
		 "idle         Lanzar un proceso que ejecuta un ciclo infinito\n"
		 "kill PID     Matar a un proceso del PID especificado\n"
		 "killall X    Matar a todos los procesos de comando X\n"
		 "ps           Ver la lista de procesos del sistema\n"
		 "top          Ver la lista de procesos del sistema con \%CPU utilizado\n"
		 "setdate      Cambiar la fecha y hora del sistema\n"
		 "shell        Cargar un interprete de comandos\n"
		 "sleep_test   Muestra un debugging del sleep del sistema\n"
		 "test_args    Muestra los argumentos de linea de comandos\n"
		 "waitpid_test Muestra un debugging del waitpid del sistema\n"
		 "malloc_test  Muestra un programa de testeo para el malloc\n");

	puts("\nPRESIONE ENTER PARA CONTINUAR...");
	getchar();
	puts("\n");

	puts("shm_test     Muestra la interaccion de dos programas por shared memory\n"
		 "fg           Vuelve un proceso que esta en background a foreground\n"
	     "reboot       Reinicia la computadora\n"
		 "priority     Setea si el algoritmo de scheduling usa prioridades o no\n"
		 "page_test    Muestra un debugging de paginacion\n"
		 "help         Muestra esta ayuda\n\n\n"
		 "Grupo: Conrado, Negro\n"
		 "       Gross, German\n"
		 "       Pampliega, Juan Martin\n\n");

	_doexit(0);
	return 0;
}

static int
validate_date(unsigned int day, unsigned int month, unsigned int year)
{
	static int days_in_month[13] = { 0, 31, 29, 31, 30, 31, 30,
					    31, 31, 30, 31, 30, 31 };

	if (!BETWEEN(year, MIN_YEAR, MAX_YEAR) || !BETWEEN(month, 1, 12)
			|| !BETWEEN(day, 1, days_in_month[month]))
			return 0;

	return ((month == 2 && day == 29) ? IS_LEAP_YEAR(year) : 1);
}

static int
int2bcd(int n)
{
	return (((n / 10) << 4) + (n % 10));
}

int
reboot(int argc, char *argv[])
{
    byte temp;

    _Cli();
    puts("Reiniciando...\n");
	puts("Cerrando la sesion.... ");
	sleep(1);
	puts("OK!\n");
	sleep(2);

    /* Borro buffers de teclado (salida y comandos) */
    do
    {
        temp = _inb(KBRD_INTRFC); 	/* Vacio data de usuario */
        if (check_flag(temp, KBRD_BIT_KDATA) != 0)
            _inb(KBRD_IO); 			/* Vacio data de teclado */
    } while (check_flag(temp, KBRD_BIT_UDATA) != 0);

	/* Reseteo CPU */
	asm volatile ("outb %1, %0" : : "dN" ((u16int)KBRD_INTRFC),
					"a" ((u8int)KBRD_RESET));
    asm volatile ("HLT");	/* Si no funciono, hago un halt */

	/* Nunca deberia llegar aca */
    _doexit(0);
    return 0;
}

int
priority(int argc, char *argv[])
{
	if(argc!=2 || (strcmp(argv[1], "on")!=0 && strcmp(argv[1], "off")!=0))
	{
		printf("%s: {on/off}\n", argv[0]);
		printf("Priority is %s\n", get_schedule_priority()?"ON":"OFF");
		_doexit(1);
		return 1;
	}

	if(!strcmp(argv[1], "on"))
		set_schedule_priority(1);
	else
		set_schedule_priority(0);
	printf("Priority is %s\n", get_schedule_priority()?"ON":"OFF");
	_doexit(0);
	return 0;

}

int
page_test(int argc, char *argv[])
{
	puts("                             INFORMACION DE PAGINAS\n");

	print_page_info();

	_doexit(0);
	return 0;
}
